using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Reflection;

namespace Sio.Mdm.Entities
{
    public static class FilterMethods
    {
        public static ISioFilter<TSource> Where<TSource>(this ISioFilter<TSource> source, Expression<Func<TSource, bool>> predicate)
        {
            return source.CreateQuery<TSource>(Expression.Call(null, ((MethodInfo)MethodBase.GetCurrentMethod()).MakeGenericMethod(new Type[] { typeof(TSource) }), new Expression[] { source.Expression, Expression.Quote(predicate) }));
        }

        public static ISioFilter<TSource> OrderBy<TSource, TKey>(this ISioFilter<TSource> source, Expression<Func<TSource, TKey>> keySelector)
        {
            return (ISioFilter<TSource>)source.CreateQuery<TSource>(Expression.Call(null, ((MethodInfo)MethodBase.GetCurrentMethod()).MakeGenericMethod(new Type[] { typeof(TSource), typeof(TKey) }), new Expression[] { source.Expression, Expression.Quote(keySelector) }));
        }

        public static ISioFilter<TSource> OrderByDescending<TSource, TKey>(this ISioFilter<TSource> source, Expression<Func<TSource, TKey>> keySelector)
        {
            return (ISioFilter<TSource>)source.CreateQuery<TSource>(Expression.Call(null, ((MethodInfo)MethodBase.GetCurrentMethod()).MakeGenericMethod(new Type[] { typeof(TSource), typeof(TKey) }), new Expression[] { source.Expression, Expression.Quote(keySelector) }));
        }

        public static ISioFilter<TSource> Skip<TSource>(this ISioFilter<TSource> source, int count)
        {
            return source.CreateQuery<TSource>(Expression.Call(null, ((MethodInfo)MethodBase.GetCurrentMethod()).MakeGenericMethod(new Type[] { typeof(TSource) }), new Expression[] { source.Expression, Expression.Constant(count) }));
        }

        public static ISioFilter<TSource> Take<TSource>(this ISioFilter<TSource> source, int count)
        {
            return source.CreateQuery<TSource>(Expression.Call(null, ((MethodInfo)MethodBase.GetCurrentMethod()).MakeGenericMethod(new Type[] { typeof(TSource) }), new Expression[] { source.Expression, Expression.Constant(count) }));
        }

        public static ISioFilter<TResult> Join<TOuter, TInner, TKey, TResult>(this ISioFilter<TOuter> outer, ISioFilter<TInner> inner, Expression<Func<TOuter, TKey>> outerKeySelector, Expression<Func<TInner, TKey>> innerKeySelector, Expression<Func<TOuter, TInner, TResult>> resultSelector)
        {
            return outer.CreateQuery<TResult>(Expression.Call(null, ((MethodInfo)MethodBase.GetCurrentMethod()).MakeGenericMethod(new Type[] { typeof(TOuter), typeof(TInner), typeof(TKey), typeof(TResult) }), new Expression[] { outer.Expression, GetSourceExpression<TInner>(inner), Expression.Quote(outerKeySelector), Expression.Quote(innerKeySelector), Expression.Quote(resultSelector) }));
        }

        public static ISioFilter<TResult> LeftJoin<TOuter, TInner, TKey, TResult>(this ISioFilter<TOuter> outer, ISioFilter<TInner> inner, Expression<Func<TOuter, TKey>> outerKeySelector, Expression<Func<TInner, TKey>> innerKeySelector, Expression<Func<TOuter, TInner, TResult>> resultSelector, Expression<Func<TOuter, TInner, bool>> additionalWhere)
        {
            return outer.CreateQuery<TResult>(Expression.Call(null, ((MethodInfo)MethodBase.GetCurrentMethod()).MakeGenericMethod(new Type[] { typeof(TOuter), typeof(TInner), typeof(TKey), typeof(TResult) }), new Expression[] { outer.Expression, GetSourceExpression<TInner>(inner), Expression.Quote(outerKeySelector), Expression.Quote(innerKeySelector), Expression.Quote(resultSelector), Expression.Quote(additionalWhere) }));
        }

        public static ISioFilter<TSource> SelectByField<TSource, TKey>(this ISioFilter<TSource> source, Expression<Func<TSource, TKey>> keySelector, IEnumerable<TKey> selectValues)
        {
            return (ISioFilter<TSource>)source.CreateQuery<TSource>(Expression.Call(null, ((MethodInfo)MethodBase.GetCurrentMethod()).MakeGenericMethod(new Type[] { typeof(TSource), typeof(TKey) }), new Expression[] { source.Expression, Expression.Quote(keySelector), Expression.Constant(selectValues) }));
        }

        public static ISioFilter<TResult> Select<TSource, TResult>(this ISioFilter<TSource> source, Expression<Func<TSource, TResult>> selector)
        {
            return source.CreateQuery<TResult>(Expression.Call(null, ((MethodInfo)MethodBase.GetCurrentMethod()).MakeGenericMethod(new Type[] { typeof(TSource), typeof(TResult) }), new Expression[] { source.Expression, Expression.Quote(selector) }));
        }
        
        private static Expression GetSourceExpression<TSource>(ISioFilter<TSource> source)
        {
            ISioFilter<TSource> queryable = source as ISioFilter<TSource>;
            if (queryable != null)
            {
                return queryable.Expression;
            }
            return Expression.Constant(source, typeof(IEnumerable<TSource>));
        }
    }
}
